/**
 * 防抖函数
 * @param func 实际执行的函数
 * @param delay 延迟时间 (ms)
 * @returns 防抖后的函数
 */
export function debounce<T extends (...args: any[]) => void>(func: T, delay: number): (...args: Parameters<T>) => void {
  let timeoutId: ReturnType<typeof setTimeout> | null = null;

  return function (this: ThisParameterType<T>, ...args: Parameters<T>): void {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    timeoutId = setTimeout(() => {
      func.apply(this, args);
      timeoutId = null;
    }, delay);
  };
};

/**
 * 节流函数
 * @param func 实际执行的函数
 * @param delay 延迟时间 (ms)
 * @returns 节流后的函数
 */
export function throttle<T extends (...args: any[]) => void>(func: T, delay: number): (...args: Parameters<T>) => void {
  let inThrottle: boolean = false;
  let lastResult: ReturnType<T>;
  let lastArgs: Parameters<T>;
  let timeoutId: ReturnType<typeof setTimeout> | null = null;

  return function (this: ThisParameterType<T>, ...args: Parameters<T>): ReturnType<T> {
    lastArgs = args;
    if (!inThrottle) {
      inThrottle = true;
      timeoutId = setTimeout(() => {
        inThrottle = false;
        if (lastArgs) { // 延迟执行后，如果还有在节流期间的最后一次调用，则再执行一次
          func.apply(this, lastArgs);
          lastArgs = null!;
        }
      }, delay);
      lastResult = func.apply(this, args); // 立即执行第一次
    }
    return lastResult;
  };
};
/**
 * 格式化时间 (例如 2024/05/18 10:30:00)
 * @param date Date对象
 * @returns 格式化后的时间字符串
 */
export const formatTime = (date: Date): string => {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();
  const hour = date.getHours();
  const minute = date.getMinutes();
  const second = date.getSeconds();

  return (
    [year, month, day].map(formatNumber).join('/') +
    ' ' +
    [hour, minute, second].map(formatNumber).join(':')
  );
};

const formatNumber = (n: number): string => {
  const s = n.toString();
  return s[1] ? s : '0' + s;
};
/**
 * 根据星期数字获取星期字符串
 * @param weekday 星期数字 (0-6, 0为星期日)
 * @returns 星期字符串，例如“星期一”
 */
export const getWeekdayString = (weekday: number): string => {
  const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  return weekdays[weekday];
};

/**
 * 获取指定日期是星期几的短字符串
 * @param date Date对象
 * @returns 例如 "周一", "周二"
 */
export const getShortWeekdayString = (date: Date): string => {
  const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
  return weekdays[date.getDay()];
};

/**
 * 生成唯一ID (UUID v4 简化版)
 * @returns 唯一ID字符串
 */
export const generateUniqueId = (): string => {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
};

/**
 * 格式化日期为 YYYY-MM-DD
 * @param date Date对象
 * @returns YYYY-MM-DD 格式字符串
 */
export const formatDateToYYYYMMDD = (date: Date): string => {
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, '0');
  const day = date.getDate().toString().padStart(2, '0');
  return `${year}-${month}-${day}`;
};

/**
 * 根据开学日期和当前日期计算当前是第几周
 * @param startDateStr 开学日期字符串 "YYYY-MM-DD"
 * @param currentDate Date对象，默认为当前时间
 * @returns 当前周数 (从1开始)，如果startDateStr无效或未来，可能返回负数或0，需调用方处理
 */
export const calculateCurrentWeek = (startDateStr: string, currentDate: Date = new Date()): number => {
  const startDate = new Date(startDateStr.replace(/-/g, '/')); // 兼容iOS日期解析
  startDate.setHours(0, 0, 0, 0); // 清除时间部分，只比较日期

  currentDate.setHours(0, 0, 0, 0);

  const diffTime = currentDate.getTime() - startDate.getTime();
  const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); // 向下取整，确保同一周内天数递增

  // 获取开学日期是星期几 (0-6, 周日为0)
  const startDayOfWeek = startDate.getDay();

  // 假设学期的第一周从开学日期所在的周的星期日开始计算
  // 那么开学日期到第一周的周日（如果开学日期在周日之后，则为开学日期所在周的周日）的偏移天数
  const daysOffsetToFirstSunday = (startDayOfWeek === 0) ? 0 : (7 - startDayOfWeek);

  // 计算从第一周的周日（或开学日如果开学日是周日）开始到当前日期的总天数
  // 如果开学日期是周日，则diffDays直接是相对于开学日的差值
  // 如果开学日期不是周日，则学期的第一周会包含开学日期之前的几天（如果存在），我们这里简化处理为开学日即为第一周的开始。
  // 更精确的计算方式是：从开学日期所在的那个星期一（或你定义的每周第一天）开始算作第一周。
  // 但为了简化，我们依然使用当前这种从开学日期开始算累计天数的方法，然后除以7向上取整。

  if (diffDays < 0) return 0; // 开学日期在当前日期之后

  // 假设开学日期是周一，那么周一到周日是第一周
  // diffDays / 7 向上取整
  return Math.floor(diffDays / 7) + 1;
};


/**
 * 获取指定周的所有日期（从周日开始，共7天）
 * @param startDateStr 开学日期字符串 "YYYY-MM-DD"
 * @param targetWeek 要获取的周数 (从1开始)
 * @returns 包含7个Date对象的数组
 */
export const getWeekDates = (startDateStr: string, targetWeek: number): Date[] => {
  const startDate = new Date(startDateStr.replace(/-/g, '/'));
  startDate.setHours(0, 0, 0, 0);

  // 计算学期的第一个星期日 (如果开学日是周日，就是开学日；否则是开学日所在的那个星期日)
  const startDayOfWeek = startDate.getDay(); // 0-6
  const firstSunday = new Date(startDate);
  if (startDayOfWeek !== 0) { // 如果开学日不是周日，则回溯到上一个周日
    firstSunday.setDate(startDate.getDate() - startDayOfWeek);
  }

  // 计算目标周的星期日
  const targetSunday = new Date(firstSunday);
  targetSunday.setDate(firstSunday.getDate() + (targetWeek - 1) * 7);

  const weekDates: Date[] = [];
  for (let i = 0; i < 7; i++) {
    const day = new Date(targetSunday);
    day.setDate(targetSunday.getDate() + i);
    weekDates.push(day);
  }
  return weekDates;
};
